﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using GeckoBrowser.Enums;
using GeckoBrowser.Utils;
using GeckoBrowser.Data;
using HtmlAgilityPack;
using MediaPortal.Configuration;
using MediaPortal.GUI.Library;
using Skybound.Gecko;
using HtmlDocument = HtmlAgilityPack.HtmlDocument;

namespace GeckoBrowser.WebBrowser
{
    public static class WebBrowserManager
    {
        #region Logger
        private static readonly Logger logger = Logger.GetInstance();
        #endregion

        #region Config
        public static string Startpage { get; set; }
        public static int DefaultZoom { get; set; }
        public static int ZoomStep { get; set; }
        public static bool MouselessBrowsing { get; set; }
        #endregion

        #region Properties
        public static bool InitializationInProgress { get; set; }
        #endregion

        #region Fields
        private static int _wbCtrlNr = 0;
        private static int _actZoomState = 100;
        #endregion

        #region Delegates
        private delegate void InitializeControlsDelegate();
        private delegate void NavigationDelegate();
        private delegate void InitGeckoDelegate();
        #endregion

        #region Gecko
        private static GeckoWebBrowser _webBrowser;
        private static GeckoElementCollection _links;
        private static GeckoElementCollection _forms;
        private static List<HtmlLinkNumber> _htmlLinkNumbers;
        #endregion

        #region Constants
        private const string _span = "<span style=\"font-family: arial,sans-serif; font-size: 12px ! important; line-height: 130% ! important; border-width: 1px ! important; border-style: solid ! important; -moz-border-radius: 2px 2px 2px 2px ! important; padding: 0px 2px ! important; margin-left: 2px; max-width: 20px; max-height: 10px ! important; overflow: visible ! important; float: none ! important; display: inline;\" gecko_id=\"{0}\" gecko_action=\"{1}\" gecko_type=\"{2}\">{0}</span>";
        #endregion


        public static bool InitWebBrowser()
        {
            string xulDir = Config.GetFolder(Config.Dir.Config) + @"\GeckoBrowser\XULRunner\";
            logger.WriteLog("Init Geckobrowser", LogLevel.Info);
            if (Directory.Exists(xulDir))
            {
                logger.WriteLog("XULRunner directory exists -> " + xulDir, LogLevel.Debug);
                Xpcom.Initialize(xulDir);
                GeckoPreferences.User["browser.xul.error_pages.enabled"] = true;
                GeckoBrowserUtils.SetProperty("#geckobrowser.link.shorturl", " ");
                GeckoBrowserUtils.SetProperty("#geckobrowser.status", " ");
                return true;
            }
            logger.WriteLog("XULRunner directory doesn't exists. Please read the readme. XULRunner should be place here: " + xulDir, LogLevel.Error);
            return false;
        }
        public static string GetStartpageLink()
        {
            logger.WriteLog("Getting startpage link...", LogLevel.Debug);
            string link = GUIPropertyManager.GetProperty("#geckobrowser.link.url");
            if (String.IsNullOrEmpty(link) && !String.IsNullOrEmpty(Startpage))
            {
                link = Startpage;
                logger.WriteLog("Using startpage link out of configuration -> " + link, LogLevel.Debug);
            }
            else if (String.IsNullOrEmpty(link) && String.IsNullOrEmpty(Startpage))
            {
                link = "http://www.google.com";
                logger.WriteLog("No startpage and property link was set. Using default link -> " + link, LogLevel.Debug);
            }
            else
            {
                logger.WriteLog("Property link was set. Using link -> " + link, LogLevel.Debug);
            }
            return link;
        }

        public static int GetDefaultZoom()
        {
            int zoomlevel = 100;
            logger.WriteLog("Getting default zoom...", LogLevel.Debug);
            try
            {
                string szoom = GUIPropertyManager.GetProperty("#geckobrowser.link.zoom");
                if (String.IsNullOrEmpty(szoom) || szoom == " ")
                {
                    zoomlevel = DefaultZoom;
                    logger.WriteLog("Using default zoom out of configuration -> " + zoomlevel, LogLevel.Debug);
                }
                else
                {
                    zoomlevel = Convert.ToInt32(szoom);
                    logger.WriteLog("Using default zoom out of property -> " + zoomlevel, LogLevel.Debug);
                }

            }
            catch (Exception)
            {
                zoomlevel = DefaultZoom;
                logger.WriteLog("Getting default zoom was unsuccessful. Using default one -> " + zoomlevel, LogLevel.Warning);
            }
            return zoomlevel;
        }
        public static void HideWebBrowser()
        {
            GUIGraphicsContext.form.Focus();
            _webBrowser.Hide();
            logger.WriteLog("Hiding GeckoBrowser", LogLevel.Debug);
        }
        public static void ShowWebBrowser()
        {
            _webBrowser.Show();
            _webBrowser.Focus();
            logger.WriteLog("Showing GeckoBrowser", LogLevel.Debug);
        }

        public static void UnsetWebBrowserWindow()
        {
            logger.WriteLog("Close GeckoBrowser", LogLevel.Info);
            _webBrowser.Dispose();
            GUIGraphicsContext.form.Controls.Remove(_webBrowser);
            GUIGraphicsContext.form.Focus();
            GeckoBrowserUtils.SetProperty("#geckobrowser.link.url", String.Empty);
            GeckoBrowserUtils.SetProperty("#geckobrowser.link.zoom", String.Empty);
            GeckoBrowserUtils.SetProperty("#geckobrowser.link.shorturl", String.Empty);
        }

        public static void SetWebBrowserWindow()
        {
            InitializationInProgress = true;
            logger.WriteLog("Open GeckoBrowser", LogLevel.Info);
            if (GUIGraphicsContext.form.InvokeRequired)
            {
                InitializeControlsDelegate d = new InitializeControlsDelegate(SetWebBrowserWindow);
                GUIGraphicsContext.form.Invoke(d);
                return;
            }

            GUIGraphicsContext.form.SuspendLayout();

            bool foundWindow = false;

            _webBrowser = new GeckoWebBrowser();

            for (int i = 0; i < GUIGraphicsContext.form.Controls.Count; i++)
            {
                if (GUIGraphicsContext.form.Controls[i].Name == "GeckoBrowser" && GUIGraphicsContext.form.Controls[i] is GeckoWebBrowser)
                {
                    _wbCtrlNr = i;
                    foundWindow = true;
                    break;
                }
            }

            if (!foundWindow)
            {
                GeckoBrowserUtils.SetProperty("#geckobrowser.linkid", "Navigate to LinkID: -");
                logger.WriteLog("GeckoBrowser opened. Setting it up", LogLevel.Debug);

                //_webBrowser = new GeckoWebBrowser();
                //_webBrowser.Location = new Point(0, 100);
                //_webBrowser.Size = new Size(GUIGraphicsContext.form.Size.Width, GUIGraphicsContext.form.Size.Height - 150);
                //_webBrowser.Name = "GeckoBrowser";
                //_webBrowser.NoDefaultContextMenu = true;
                ////_webBrowser.IsScrollbarXEnabled = false;
                ////_webBrowser.IsScrollbarYEnabled = false;
                //_webBrowser.DocumentCompleted += new EventHandler(WebBrowser_DocumentCompleted);
                //_webBrowser.StatusTextChanged += new EventHandler(WebBrowser_StatusTextChanged);
                //_webBrowser.DomKeyDown += new GeckoDomKeyEventHandler(_webBrowser_DomKeyDown);
                //_webBrowser.CreateWindow += new GeckoCreateWindowEventHandler(_webBrowser_CreateWindow);
                //_webBrowser.DomClick += new GeckoDomEventHandler(_webBrowser_DomClick);
                //GUIGraphicsContext.form.Controls.Add(_webBrowser);
                //GUIGraphicsContext.form.Controls[_wbCtrlNr].Enabled = false;
                Thread initThread = new Thread(new ThreadStart(InitGecko));
                initThread.SetApartmentState(ApartmentState.STA);
                initThread.Priority = ThreadPriority.AboveNormal;
                initThread.IsBackground = true;
                initThread.Start();

                GUIGraphicsContext.MouseSupport = true;
            }
            else
            {
                logger.WriteLog("GeckoBrowser is already opened. Doing nothing", LogLevel.Debug);
            }

            GUIGraphicsContext.form.ResumeLayout();
        }
        static void InitGecko()
        {
            _webBrowser = new GeckoWebBrowser();
            _webBrowser.Location = new Point(0, 100);
            _webBrowser.Size = new Size(GUIGraphicsContext.form.Size.Width, GUIGraphicsContext.form.Size.Height - 150);
            _webBrowser.Name = "GeckoBrowser";
            _webBrowser.NoDefaultContextMenu = true;
            //_webBrowser.IsScrollbarXEnabled = false;
            //_webBrowser.IsScrollbarYEnabled = false;
            _webBrowser.DocumentCompleted += new EventHandler(WebBrowser_DocumentCompleted);
            _webBrowser.StatusTextChanged += new EventHandler(WebBrowser_StatusTextChanged);
            _webBrowser.DomKeyDown += new GeckoDomKeyEventHandler(_webBrowser_DomKeyDown);
            _webBrowser.CreateWindow += new GeckoCreateWindowEventHandler(_webBrowser_CreateWindow);
            _webBrowser.DomClick += new GeckoDomEventHandler(_webBrowser_DomClick);
            InitGeckoDelegate igd = delegate
            {
                GUIGraphicsContext.form.Controls.Add(_webBrowser);
                GUIGraphicsContext.form.Controls[_wbCtrlNr].Enabled = false;
            };
            GUIGraphicsContext.form.BeginInvoke(igd);

            while (_webBrowser.IsBusy)
            {
                Application.DoEvents();
            }
            InitializationInProgress = false;
            

        }
        

        static void _webBrowser_DomClick(object sender, GeckoDomEventArgs e)
        {
            if (e.Target.TagName.ToUpper() == "INPUT")
            {
                if (e.Target.GetAttribute("type") != null)
                {
                    if (e.Target.GetAttribute("type").ToLower() == "text")
                    {
                        string input = String.Empty;
                        
                        _webBrowser.Hide();
                        bool confirmed = GeckoBrowserUtils.ShowKeyboard(ref input, false);
                        if (confirmed)
                        {
                            e.Target.SetAttribute("value", input);
                        }
                        _webBrowser.Show();
                    }
                }
            }
        }

        static void _webBrowser_CreateWindow(object sender, GeckoCreateWindowEventArgs e)
        {
            //e.WebBrowser = null;
            
        }

        static void _webBrowser_DomKeyDown(object sender, GeckoDomKeyEventArgs e)
        {
            GUIGraphicsContext.form.Controls[_wbCtrlNr].Enabled = false;
            GUIGraphicsContext.form.Focus();
        }

        public static void SuspendBrowser()
        {
            GUIGraphicsContext.form.Controls[_wbCtrlNr].SuspendLayout();
        }

        public static void ResumeBrowser()
        {
            GUIGraphicsContext.form.Controls[_wbCtrlNr].ResumeLayout();
        }

        public static void GoBack()
        {
            _webBrowser.GoBack();
        }
        public static void GoForward()
        {
            _webBrowser.GoForward();
        }
        public static void RefreshBrowser()
        {
            _webBrowser.Reload();
        }
        public static void StopBrowser()
        {
            _webBrowser.Stop();
        }
        public static void GoHome()
        {
            Navigate(Startpage, GetDefaultZoom());
        }

        private static void NaviThread(object data)
        {
            if (_webBrowser.IsHandleCreated)
            {
                string url = (string) data;
                if (!string.IsNullOrEmpty(url))
                {
                    NavigationDelegate nd = delegate
                                                {
                                                    _webBrowser.Navigate(url);
                                                };
                    GUIGraphicsContext.form.BeginInvoke(nd);
                    if (!url.StartsWith("javascript:"))
                    {
                        if (!url.StartsWith("http://") && !url.StartsWith("https://") && !url.StartsWith("ftp://") && !url.StartsWith("ftps://")) url = "http://" + url;
                        Uri uri = new Uri(url);
                        GeckoBrowserUtils.SetProperty("#geckobrowser.link.shorturl", uri.Scheme + "://" + uri.Host);
                        logger.WriteLog("Navigate to " + url, LogLevel.Info);
                    }
                    
                }
                while (_webBrowser.IsBusy)
                {
                    Application.DoEvents();
                }
            }
        }


        public static void Navigate(string url)
        {
            if (!url.StartsWith("javascript:"))
            {
                Thread naviThread = new Thread(new ParameterizedThreadStart(NaviThread));
                naviThread.SetApartmentState(ApartmentState.STA);
                naviThread.Priority = ThreadPriority.AboveNormal;
                naviThread.IsBackground = true;
                naviThread.Start(url);
            }
            else
            {
                _webBrowser.Navigate(url);
            }
        }
        public static void Navigate(string url, int zoomPercentage)
        {
            Navigate(url);
            Zoom(zoomPercentage);
            logger.WriteLog("Navigate to " + url + " with zoom " + zoomPercentage, LogLevel.Info);
        }



        public static void Zoom(int zoomPercentage)
        {
            if (_webBrowser.IsHandleCreated)
            {
                float fZoom = (float) (zoomPercentage/100.0);
                _webBrowser.Zoom(fZoom);
                _actZoomState = zoomPercentage;
                _webBrowser.IsScrollbarXEnabled = false;
                _webBrowser.IsScrollbarYEnabled = false;
            }
        }
        public static void ZoomOut(int zoomPercentage)
        {
            if (zoomPercentage <= 100 && zoomPercentage >= 0) Zoom(_actZoomState - zoomPercentage);
        }
        public static void ZoomIn(int zoomPercentage)
        {
            if (zoomPercentage <= 300 && zoomPercentage >= -100) Zoom(_actZoomState + zoomPercentage);
        }

        public static void ScrollDown(int step)
        {
            if (_webBrowser.Window != null) _webBrowser.Window.ScrollTo(_webBrowser.Window.ScrollX, _webBrowser.Window.ScrollY + step);
        }
        public static void ScrollUp(int step)
        {
            if (_webBrowser.Window != null) _webBrowser.Window.ScrollTo(_webBrowser.Window.ScrollX, _webBrowser.Window.ScrollY - step);
        }
        public static void ScrollLeft(int step)
        {
            if (_webBrowser.Window != null) _webBrowser.Window.ScrollTo(_webBrowser.Window.ScrollX - step, _webBrowser.Window.ScrollY);
        }
        public static void ScrollRight(int step)
        {
            if (_webBrowser.Window != null) _webBrowser.Window.ScrollTo(_webBrowser.Window.ScrollX + step, _webBrowser.Window.ScrollY);
        }

        public static void SetInputFieldText(int id, string text)
        {


            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(_webBrowser.Document.Body.InnerHtml);

            foreach (HtmlNode element in doc.DocumentNode.SelectNodes("//input"))
            {

                string name = element.GetAttributeValue("gb", "");
                if (!string.IsNullOrEmpty(name))
                {
                    if (name == "gecko_link" + id)
                    {
                        element.SetAttributeValue("value", text);
                        _webBrowser.Document.Body.InnerHtml = doc.DocumentNode.InnerHtml;
                        break;

                    }
                }
            }
        }

        public static bool GetLinkById(int value, out HtmlLinkNumber hln)
        {
            if (_htmlLinkNumbers != null && _htmlLinkNumbers.Count > 0)
            {
                foreach (HtmlLinkNumber id in _htmlLinkNumbers)
                {
                    if (id.Number == value)
                    {
                        switch (id.Type)
                        {
                            case HtmlInputType.Link:
                                {
                                    Uri uri;
                                    if (Uri.TryCreate(_webBrowser.Url, id.Link, out uri))
                                    {
                                        id.Link = uri.AbsoluteUri;
                                        hln = id;
                                        return true;
                                    }
                                }
                                break;
                            case HtmlInputType.Input:
                            case HtmlInputType.Action:
                                hln = id;
                                return true;
                                //return "javascript:document.getElementById(\"" + id.Name + "\").click()";
                        }
                    }
                }
            }
            hln = null;
            return false;
        }

        static void WebBrowser_StatusTextChanged(object sender, EventArgs e)
        {
            GeckoBrowserUtils.SetProperty("#geckobrowser.status", _webBrowser.StatusText != string.Empty ? _webBrowser.StatusText : "Done.");
        }
        
        static void WebBrowser_DocumentCompleted(object sender, EventArgs e)
        {
            if (MouselessBrowsing)
            {
                _htmlLinkNumbers = new List<HtmlLinkNumber>();
                _links = _webBrowser.Document.Links;
                int i = 1;
                foreach (GeckoElement element in _links)
                {
                    string link = element.GetAttribute("href");

                    if (!link.StartsWith("javascript:"))
                    {

                        element.InnerHtml += string.Format(_span, i, "", "LINK");
                        string gb = element.GetAttribute("gb");
                        string id = element.GetAttribute("id");
                        string name = element.GetAttribute("name");
                        if (string.IsNullOrEmpty(gb))
                        {
                            element.SetAttribute("gb", "gecko_link" + i);
                        }
                        if (string.IsNullOrEmpty(id))
                        {
                            element.SetAttribute("id", "gb" + i);
                            id = "gb" + i;
                        }
                        _htmlLinkNumbers.Add(new HtmlLinkNumber(i, id, name, link, HtmlInputType.Link));
                        i++;
                    }
                }
                _forms = _webBrowser.Document.GetElementsByTagName("form");
                HtmlDocument doc = new HtmlDocument();
                foreach (GeckoElement element in _forms)
                {
                    string action = element.GetAttribute("action");
                    doc.LoadHtml(element.InnerHtml);
                    foreach (HtmlNode link in doc.DocumentNode.SelectNodes("//*"))
                    {
                        if (link.OriginalName == "input")
                        {
                            if (link.Attributes["type"] != null)
                            {
                                if (link.Attributes["type"].Value != "hidden")
                                {

                                    string gb = link.GetAttributeValue("gb", "");
                                    string id = link.GetAttributeValue("id", "");
                                    string name = link.GetAttributeValue("name", "");
                                    string outerHtml = link.OuterHtml;
                                    if (string.IsNullOrEmpty(gb))
                                    {
                                        link.SetAttributeValue("gb", "gecko_link" + i);
                                    }
                                    if (string.IsNullOrEmpty(id))
                                    {
                                        link.SetAttributeValue("id", "gb" + i);
                                        id = "gb" + i;
                                    }
                                    string newLink = link.OuterHtml + string.Format(_span, i, action, "INPUT");
                                    element.InnerHtml = element.InnerHtml.Replace(outerHtml, newLink);


                                    if(link.Attributes["type"].Value == "submit" ||
                                        link.Attributes["type"].Value == "reset" ||
                                        link.Attributes["type"].Value == "radio" ||
                                        link.Attributes["type"].Value == "image" ||
                                        link.Attributes["type"].Value == "checkbox" )
                                    {
                                        _htmlLinkNumbers.Add(new HtmlLinkNumber(i, id, name, action, HtmlInputType.Action));
                                    }
                                    else
                                    {
                                        _htmlLinkNumbers.Add(new HtmlLinkNumber(i, id, name, action, HtmlInputType.Input));
                                    }
                                    i++;
                                }
                            }
                            else
                            {
                                string gb = link.GetAttributeValue("gb", "");
                                string id = link.GetAttributeValue("id", "");
                                string name = link.GetAttributeValue("name", "");
                                string outerHtml = link.OuterHtml;
                                if (string.IsNullOrEmpty(gb))
                                {
                                    link.SetAttributeValue("gb", "gecko_link" + i);
                                }
                                if (string.IsNullOrEmpty(id))
                                {
                                    link.SetAttributeValue("id", "gb" + i);
                                    id = "gb" + i;
                                }
                                string newLink = link.OuterHtml + string.Format(_span, i, action, "INPUT");
                                element.InnerHtml = element.InnerHtml.Replace(outerHtml, newLink);
                                _htmlLinkNumbers.Add(new HtmlLinkNumber(i, id, name, action, HtmlInputType.Input));
                                i++;
                            }

                        }
                    }
                }
            }
        }
        public static void Focus()
        {
            GUIGraphicsContext.form.Controls[_wbCtrlNr].Enabled = true;
            _webBrowser.Focus();
        }
    }
}
